package com.yykj.hospital.server.service.banci.impl;

import com.yykj.base.client.dto.WordBriefInfo;
import com.yykj.base.client.feign.BaseFeignClient;
import com.yykj.common.constant.CommonConstant;
import com.yykj.common.enums.BanCiType;
import com.yykj.common.enums.ResultEnums;
import com.yykj.common.exception.NormalException;
import com.yykj.common.util.GsonUtil;
import com.yykj.common.util.LocalObjectUtil;
import com.yykj.common.util.PageRequestUtil;
import com.yykj.common.vo.PageData;
import com.yykj.hospital.client.dto.DoctorAvailableOnlineDTO;
import com.yykj.hospital.client.dto.DoctorDTO;
import com.yykj.hospital.client.dto.banci.DoctorAvailableAppointmentDTO;
import com.yykj.hospital.client.dto.banci.HospitalBanCiDTO;
import com.yykj.hospital.client.form.OnlineOrderForm;
import com.yykj.hospital.client.form.banci.AppointmentOrderForm;
import com.yykj.hospital.client.form.banci.BanCIListFrom;
import com.yykj.hospital.client.form.banci.BanCiEditForm;
import com.yykj.hospital.server.converter.DoctorVisitDTOConverter;
import com.yykj.hospital.server.converter.DoctorVisitLevelDTOConverter;
import com.yykj.hospital.server.converter.HospitalBanCiDTOConverter;
import com.yykj.hospital.server.dto.banci.*;
import com.yykj.hospital.server.form.DoctorBanCiEditForm;
import com.yykj.hospital.server.form.DoctorVisitForm;
import com.yykj.hospital.server.form.DoctorVisitLevelForm;
import com.yykj.hospital.server.form.DoctorVisitSettingSearchForm;
import com.yykj.hospital.server.model.entity.HospitalDepartment;
import com.yykj.hospital.server.model.entity.HospitalDoctor;
import com.yykj.hospital.server.model.entity.banci.HospitalBanCi;
import com.yykj.hospital.server.model.entity.banci.HospitalDoctorBanCi;
import com.yykj.hospital.server.model.entity.banci.HospitalDoctorVisitLevel;
import com.yykj.hospital.server.model.entity.banci.HospitalDoctorVisitSetting;
import com.yykj.hospital.server.model.repository.HospitalDepartmentRepository;
import com.yykj.hospital.server.model.repository.HospitalDoctorRepository;
import com.yykj.hospital.server.model.repository.banci.HospitalBanCiRepository;
import com.yykj.hospital.server.model.repository.banci.HospitalDoctorBanCiRepository;
import com.yykj.hospital.server.model.repository.banci.HospitalDoctorVisitLevelRepository;
import com.yykj.hospital.server.model.repository.banci.HospitalDoctorVisitSettingRepository;
import com.yykj.hospital.server.service.HospitalDoctorService;
import com.yykj.hospital.server.service.banci.BanCiService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.*;
import javax.transaction.Transactional;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Slf4j
@Service
public class BanCiServiceImpl implements BanCiService {
    @Override
    public HospitalBanCiDTO editBanCi(BanCiEditForm form) {
        HospitalBanCi hospitalBanCi;
        if (form.getId() != null && form.getId() > 0) {
            hospitalBanCi = banCiRepository.findById(form.getId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        } else {
            HospitalBanCi firstByCode = banCiRepository.findFirstByHospitalIdAndCodeAndType(form.getHospitalId(), form.getCode(), form.getType());
            if (firstByCode != null) {
                throw new NormalException(ResultEnums.ALL_READY_EXIST, "Ban Ci Code 已经存在");
            }
            hospitalBanCi = new HospitalBanCi();
        }
        BeanUtils.copyProperties(form, hospitalBanCi);
        LocalTime time;
        if (!StringUtils.isEmpty(form.getStartTime())) {
            time = LocalTime.parse(form.getStartTime(), DateTimeFormatter.ofPattern("HH:mm"));
            hospitalBanCi.setStartTime(time);
        }

        if (!StringUtils.isEmpty(form.getEndTime())) {
            time = LocalTime.parse(form.getEndTime(), DateTimeFormatter.ofPattern("HH:mm"));
            hospitalBanCi.setEndTime(time);
        }

        if (!StringUtils.isEmpty(form.getName())) {
            WordBriefInfo wordBriefInfo = baseFeignClient.getWordBriefInfo(form.getName());
            hospitalBanCi.setWb(wordBriefInfo.getWb());
            hospitalBanCi.setPy(wordBriefInfo.getPy());
        }
        hospitalBanCi = banCiRepository.save(hospitalBanCi);
        return HospitalBanCiDTOConverter.convert(hospitalBanCi);
    }

    @Override
    public List<HospitalBanCiDTO> listBanCi(Integer hospitalId, BanCiType type, String keyword) {
        if (hospitalId == null || hospitalId == 0 || type == null) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }

        List<HospitalBanCi> banCiList = banCiRepository.findAll(new Specification<HospitalBanCi>() {
            @Override
            public Predicate toPredicate(Root<HospitalBanCi> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();
                predicateList.add(builder.equal(root.get("hospitalId"), hospitalId));
                predicateList.add(builder.equal(root.get("type"), type));
                if (!StringUtils.isEmpty(keyword)) {
                    Predicate p1 = builder.like(root.get("name"), "%" + keyword + "%");
                    Predicate p2 = builder.like(root.get("code"), "%" + keyword + "%");
                    Predicate p3 = builder.like(root.get("py"), "%" + keyword + "%");
                    Predicate p4 = builder.like(root.get("wb"), "%" + keyword + "%");
                    predicateList.add(builder.or(p1, p2, p3, p4));
                }
                Order timeOrder = builder.asc(root.get("startTime"));
                Predicate[] predicateArray = new Predicate[predicateList.size()];
                return query.orderBy(timeOrder).where(predicateList.toArray(predicateArray)).getRestriction();
            }
        });

        return HospitalBanCiDTOConverter.convert(banCiList);
    }

    @Override
    public DoctorVisitSettingDTO editDoctorVisit(DoctorVisitForm form) {
        if(form.getHospitalId() == null || form.getHospitalId() < 0 || form.getBanCiId() == null || form.getBanCiId()<0 || StringUtils.isEmpty(form.getDoctorId()))  {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }

        HospitalDoctorVisitSetting setting = hospitalDoctorVisitSettingRepository.findFirstByHospitalIdAndDoctorIdAndBanCiId(form.getHospitalId(),
                form.getDoctorId(),form.getBanCiId());
        if(setting == null){
            setting = new HospitalDoctorVisitSetting();
        }

        LocalObjectUtil.copyNullValue(form, setting);

        if(!StringUtils.isEmpty(form.getStartTime())){
            LocalTime startTime =  LocalTime.parse(form.getStartTime(), DateTimeFormatter.ofPattern("HH:mm"));
            setting.setStartTime(startTime);
        }
        if(!StringUtils.isEmpty(form.getEndTime())){
            LocalTime endTime =  LocalTime.parse(form.getEndTime(), DateTimeFormatter.ofPattern("HH:mm"));
            setting.setEndTime(endTime);
        }
        Map<Integer, HospitalBanCi> hospitalBanCiMap = getHospitalBanCiIdMap(form.getHospitalId(), form.getType());
        HospitalBanCi hospitalBanCi = hospitalBanCiMap.get(form.getBanCiId());
        if (hospitalBanCi == null) {
            throw new NormalException(ResultEnums.HOSPITAL_SECLUDING_NOT_EXIST);
        }
        setting.setBanCiId(hospitalBanCi.getId());

        DoctorDTO info = doctorService.info(form.getDoctorId());
        if (info == null) {
            throw new NormalException(ResultEnums.NOT_EXIST);
        }
        setting.setDepId(info.getDepId());
        setting.setDoctorName(info.getDoctorName());
        setting.setPy(info.getPy());
        setting.setWb(info.getWb());

        setting = hospitalDoctorVisitSettingRepository.save(setting);
        return DoctorVisitDTOConverter.convert(setting);
    }

    @Override
    public List<DoctorVisitSettingDTO> listDoctorVisit(Integer hospitalId, Integer depId, String keyword) {
        if (hospitalId == null || hospitalId == 0) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }
        List<HospitalDoctorVisitSetting> settingList;
        if ((depId == null || depId == 0) && StringUtils.isEmpty(keyword)) {
            settingList = hospitalDoctorVisitSettingRepository.findAll();
        } else {
            settingList = hospitalDoctorVisitSettingRepository.findAll(new Specification<HospitalDoctorVisitSetting>() {
                @Override
                public Predicate toPredicate(Root<HospitalDoctorVisitSetting> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                    List<Predicate> predicateList = new ArrayList<>();
                    if (depId != null && hospitalId > 0) {
                        predicateList.add(builder.equal(root.get("depId"), depId));
                    }
                    if (!StringUtils.isEmpty(keyword)) {
                        Predicate p1 = builder.like(root.get("banCiCode"), "%" + keyword + "%");
                        Predicate p2 = builder.like(root.get("banCiName"), "%" + keyword + "%");
                        Predicate p3 = builder.like(root.get("doctorName"), "%" + keyword + "%");
                        Predicate p4 = builder.like(root.get("depName"), "%" + keyword + "%");
                        predicateList.add(builder.or(p1, p2, p3, p4));
                    }
                    Predicate[] predicates = new Predicate[predicateList.size()];
                    return query.where(predicateList.toArray(predicates)).getRestriction();
                }
            });
        }

        return DoctorVisitDTOConverter.convert(settingList);
    }

    @Override
    public DoctorVisitLevelDTO editDoctorVisitLevel(DoctorVisitLevelForm form) {
        HospitalDoctorVisitLevel doctorHospitalDoctorVisitLevel;
        if (form.getId() != null && form.getId() > 0) {
            doctorHospitalDoctorVisitLevel = visitLevelRepository.findById(form.getId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        } else {
            doctorHospitalDoctorVisitLevel = new HospitalDoctorVisitLevel();
        }
        BeanUtils.copyProperties(form, doctorHospitalDoctorVisitLevel);
        if (!StringUtils.isEmpty(form.getName())) {
            WordBriefInfo wordBriefInfo = baseFeignClient.getWordBriefInfo(form.getName());
            doctorHospitalDoctorVisitLevel.setWb(wordBriefInfo.getWb());
            doctorHospitalDoctorVisitLevel.setPy(wordBriefInfo.getPy());
        }
        doctorHospitalDoctorVisitLevel = visitLevelRepository.save(doctorHospitalDoctorVisitLevel);
        return DoctorVisitLevelDTOConverter.convert(doctorHospitalDoctorVisitLevel);
    }

    @Override
    public List<DoctorVisitLevelDTO> listDoctorVisitLevel(Integer hospitalId, Boolean status, String keyword) {
        List<HospitalDoctorVisitLevel> list;
        if (!StringUtils.isEmpty(keyword)) {
            list = visitLevelRepository.findAll(new Specification<HospitalDoctorVisitLevel>() {
                @Override
                public Predicate toPredicate(Root<HospitalDoctorVisitLevel> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                    List<Predicate> predicateList = new ArrayList<>();
                    predicateList.add(builder.equal(root.get("hospitalId"), hospitalId));
                    if (status != null) {
                        predicateList.add(builder.equal(root.get("status"), status));
                    }
                    if (!StringUtils.isEmpty(keyword)) {
                        Predicate p1 = builder.like(root.get("name"), "%" + keyword + "%");
                        Predicate p2 = builder.like(root.get("code"), "%" + keyword + "%");
                        Predicate p3 = builder.like(root.get("py"), "%" + keyword + "%");
                        Predicate p4 = builder.like(root.get("wb"), "%" + keyword + "%");
                        predicateList.add(builder.or(p1, p2, p3, p4));
                    }
                    Predicate[] predicates = new Predicate[predicateList.size()];
                    return query.where(predicateList.toArray(predicates)).getRestriction();
                }
            });
        } else {
            list = visitLevelRepository.findAllByHospitalIdAndStatus(hospitalId, status);
        }
        return DoctorVisitLevelDTOConverter.convert(list);
    }

    @Override
    public List<DoctorBanCiDTO> editDoctorBanCi(DoctorBanCiEditForm form) {
        Map<Integer, HospitalBanCi> map = getHospitalBanCiIdMap(form.getHospitalId(), form.getType());

        List<HospitalDoctorVisitSetting> hospitalDoctorVisitSettingList = hospitalDoctorVisitSettingRepository.findAllByHospitalIdAndType(form.getHospitalId(),form.getType());
        Map<String, Map<Integer, HospitalDoctorVisitSetting>> visitMap = new HashMap<>();

        for (HospitalDoctorVisitSetting setting : hospitalDoctorVisitSettingList) {
            Map<Integer, HospitalDoctorVisitSetting> orDefault = visitMap.getOrDefault(setting.getDoctorId(), new HashMap<>());
            orDefault.put(setting.getBanCiId(), setting);
            visitMap.put(setting.getDoctorId(), orDefault);
        }

        List<HospitalDoctorBanCi> all = new ArrayList<>();
        for (DoctorBanCiDTO doctorBanCiDTO : form.getList()) {
            HospitalDoctorBanCi hospitalDoctorBanCi = new HospitalDoctorBanCi();
            hospitalDoctorBanCi.setHospitalId(form.getHospitalId());
            hospitalDoctorBanCi.setBanCiType(form.getType());
            hospitalDoctorBanCi.setDoctorId(doctorBanCiDTO.getDoctorId());
            hospitalDoctorBanCi.setDoctorName(doctorBanCiDTO.getDoctorName());
            for (DoctorBanCiDetailDTO detailDTO : doctorBanCiDTO.getData()) {
                HospitalBanCi hospitalBanCi = map.get(detailDTO.getBanCiId());
                if (null == hospitalBanCi) {
                    throw new NormalException(ResultEnums.HOSPITAL_SECLUDING_NOT_EXIST);
                }
                for (DailyBanCiDetailDTO daily : detailDTO.getData()) {
                    Map<Integer, HospitalDoctorVisitSetting> settingMap = visitMap.get(doctorBanCiDTO.getDoctorId());
                    if (settingMap == null) {//没有说明号源
                        DoctorDTO info = doctorService.info(doctorBanCiDTO.getDoctorId());
                        throw new NormalException(ResultEnums.NOT_ENOUGH_SCHEDULING,
                                "[" + info.getDoctorName() + "]" + ResultEnums.NOT_ENOUGH_SCHEDULING.getMessage());
                    }
                    HospitalDoctorVisitSetting setting = settingMap.get(detailDTO.getBanCiId());
                    HospitalDoctorBanCi item;
                    BeanUtils.copyProperties(setting, hospitalDoctorBanCi);
                    hospitalDoctorBanCi.setId(null);

                    List<HospitalDoctorBanCi> oldBanCiList = hospitalDoctorBanCiRepository.findAllByHospitalIdAndDoctorIdAndDayAndBanCiId(form.getHospitalId(),doctorBanCiDTO.getDoctorId(),daily.getDay(),detailDTO.getBanCiId());
                    List<HospitalDoctorBanCi> currentDayBanCi = new ArrayList<>();

                    if (hospitalBanCi.getSegment()) {
                        int min = hospitalBanCi.getEndTime().getHour() * 60 + hospitalBanCi.getEndTime().getMinute() -
                                (hospitalBanCi.getStartTime().getHour() * 60 + hospitalBanCi.getStartTime().getMinute());
                        int duration = (hospitalBanCi.getDuration() == null || hospitalBanCi.getDuration() <= 0) ? CommonConstant.DEFAULT_DURATION : hospitalBanCi.getDuration();
                        int cnt = min / duration + (min % duration == 0 ? 0 : 1);//多少个时间段
                        int tcntAppoint = setting.getAppointment() == null || setting.getAppointment() == 0 ? 0 : setting.getAppointment();
                        int tcntPic = setting.getPic() == null || setting.getPic() == 0 ? 0 : setting.getPic();
                        int tcntVoice = setting.getVoice() == null || setting.getVoice() == 0 ? 0 : setting.getVoice();
                        int tcntVideo = setting.getVideo() == null || setting.getVideo() == 0 ? 0 : setting.getVideo();
                        if (Boolean.FALSE.equals(daily.getAppointment())) {
                            tcntAppoint = 0;
                        }
                        if (Boolean.FALSE.equals(daily.getPic())) {
                            tcntPic = 0;
                        }
                        if (Boolean.FALSE.equals(daily.getVoice())) {
                            tcntVoice = 0;
                        }
                        if (Boolean.FALSE.equals(daily.getVideo())) {
                            tcntVideo = 0;
                        }

                        int ecntAppoint = tcntAppoint / cnt;
                        int ecntPic = tcntPic / cnt;

                        LocalTime time = hospitalBanCi.getStartTime();

                        for (int i = 1; i <= cnt; i++) {

                            hospitalDoctorBanCi.setStartTime(time);
                            hospitalDoctorBanCi.setEndTime(time.plusMinutes(duration));
                            hospitalDoctorBanCi.setDay(daily.getDay());
                            if (tcntAppoint >= ecntAppoint) {
                                if (i == cnt) {
                                    hospitalDoctorBanCi.setAppointment(tcntAppoint);
                                } else {
                                    hospitalDoctorBanCi.setAppointment(ecntAppoint);
                                    tcntAppoint = tcntAppoint - ecntAppoint;
                                }
                            } else {
                                hospitalDoctorBanCi.setAppointment(tcntAppoint);
                                tcntAppoint = 0;
                            }
                            if (tcntPic >= ecntPic) {
                                if (i == cnt) {
                                    hospitalDoctorBanCi.setPic(tcntPic);
                                } else {
                                    hospitalDoctorBanCi.setPic(ecntPic);
                                    tcntPic = tcntPic - ecntPic;
                                }

                            } else {
                                hospitalDoctorBanCi.setPic(tcntPic);
                                tcntPic = 0;
                            }

                            hospitalDoctorBanCi.setVoice(tcntVoice);

                            hospitalDoctorBanCi.setVideo(tcntVideo);

                            item = new HospitalDoctorBanCi();
                            BeanUtils.copyProperties(hospitalDoctorBanCi, item);

                            HospitalDoctorBanCi oldDoctorBanCi = hospitalDoctorBanCiRepository.findFirstByDoctorIdAndDayAndStartTimeAndEndTimeAndBanCiId(hospitalDoctorBanCi.getDoctorId(),
                                    hospitalDoctorBanCi.getDay(), hospitalDoctorBanCi.getStartTime(), hospitalDoctorBanCi.getEndTime(),
                                    hospitalDoctorBanCi.getBanCiId());

                            if(oldDoctorBanCi != null){
                                item.setId(oldDoctorBanCi.getId());
                                item.setCreateTime(oldDoctorBanCi.getCreateTime());
                            }
                            time = time.plusMinutes(duration);
                            all.add(item);
                            currentDayBanCi.add(item);
                        }
                    } else {
                        if (daily.getAppointment() ==null || daily.getAppointment()) {
                            hospitalDoctorBanCi.setAppointment(0);
                        }
                        hospitalDoctorBanCi.setStartTime(hospitalBanCi.getStartTime());
                        hospitalDoctorBanCi.setEndTime(hospitalBanCi.getEndTime());
                        hospitalDoctorBanCi.setDay(daily.getDay());

                        item = new HospitalDoctorBanCi();
                        BeanUtils.copyProperties(hospitalDoctorBanCi, item);
                        all.add(item);
                        currentDayBanCi.add(item);
                    }
                    if(oldBanCiList!=null && oldBanCiList.size()>0){
                        for (HospitalDoctorBanCi oldBanCi:oldBanCiList){
                            for (HospitalDoctorBanCi current:currentDayBanCi){
                                if(oldBanCi.compareTo(current) == 0){
                                   BeanUtils.copyProperties(oldBanCi,current);
                                }
                            }
                        }
                    }
                }
            }

        }
        if (all.size() > 0) {
            hospitalDoctorBanCiRepository.saveAll(all);
        }
        return null;
    }

    @Override
    public PageData<DoctorVisitSettingDTO> listDoctorVisitSetting(DoctorVisitSettingSearchForm form) {
        if (form.getHospitalId() == null || form.getHospitalId() <= 0) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }
        PageRequest pageRequest = PageRequest.of(PageRequestUtil.getPage(form.getCurrentPage()), PageRequestUtil.getSize(form.getPageSize()), Sort.by(Sort.Order.asc("doctorId"),Sort.Order.asc("banCiId")));

        Page<HospitalDoctorVisitSetting> list = hospitalDoctorVisitSettingRepository.findAll(new Specification<HospitalDoctorVisitSetting>() {
            @Override
            public Predicate toPredicate(Root<HospitalDoctorVisitSetting> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();

                predicateList.add(builder.equal(root.get("hospitalId"), form.getHospitalId()));

                predicateList.add(builder.equal(root.get("type"), form.getType()));

                if (!StringUtils.isEmpty(form.getDoctorId())) {
                    predicateList.add(builder.equal(root.get("doctorId"), form.getDoctorId()));
                }
                if (form.getDepId() != null && form.getDepId() > 0) {
                    predicateList.add(builder.equal(root.get("depId"), form.getDepId()));
                }
                if (form.getLevelId() != null && form.getLevelId() > 0) {
                    predicateList.add(builder.equal(root.get("levelId"), form.getLevelId()));
                }
                if (!StringUtils.isEmpty(form.getKeyword())) {
                    Predicate p1 = builder.like(root.get("doctorName"), "%" + form.getKeyword() + "%");
                    Predicate p2 = builder.like(root.get("py"), "%" + form.getKeyword() + "%");
                    Predicate p3 = builder.like(root.get("wb"), "%" + form.getKeyword() + "%");
                    predicateList.add(builder.or(p1, p2, p3));
                }

                Predicate[] predicates = new Predicate[predicateList.size()];
                return query.where(predicateList.toArray(predicates)).getRestriction();
            }
        }, pageRequest);

        PageData<HospitalDoctorVisitSetting> settingPageData = PageData.formatList(list, form.getCurrentPage(), form.getPageSize());
        PageData<DoctorVisitSettingDTO> result = new PageData<>();
        result.setTotalCount(settingPageData.getTotalCount());
        result.setTotalPage(settingPageData.getTotalPage());
        result.setPageSize(settingPageData.getPageSize());
        result.setCurrentPage(settingPageData.getCurrentPage());
        List<DoctorVisitSettingDTO> dtoList = new ArrayList<>();
        List<HospitalDepartment> departmentList = departmentRepository.findAllByHospitalId(form.getHospitalId());
        Map<Integer, HospitalDepartment> departmentMap = new HashMap<>();
        for (HospitalDepartment department : departmentList) {
            departmentMap.put(department.getId(), department);
        }
        for (HospitalDoctorVisitSetting setting : list.getContent()) {
            DoctorVisitSettingDTO dto = new DoctorVisitSettingDTO();
            BeanUtils.copyProperties(setting, dto);
            HospitalDepartment department = departmentMap.get(setting.getDepId());
            dto.setDepIds(Arrays.asList(department.getPid(), department.getId()));
            dtoList.add(dto);
        }
        result.setList(dtoList);
        return result;

    }


    @Override
    public void initDefaultHospitalBanCi(Integer hospitalId) {
        //添加默认上午和下午班次
        BanCiEditForm form = BanCiEditForm.builder()
                .hospitalId(hospitalId)
                .startTime("08:00")
                .endTime("11:30")
                .name("上午")
                .code("am")
                .status(Boolean.TRUE)
                .segment(Boolean.FALSE)
                .type(BanCiType.online)
                .build();
        this.editBanCi(form);

        form.setType(BanCiType.appointment);
        this.editBanCi(form);

        form = BanCiEditForm.builder()
                .hospitalId(hospitalId)
                .startTime("13:00")
                .endTime("16:00")
                .name("下午")
                .code("pm")
                .status(Boolean.TRUE)
                .type(BanCiType.online)
                .segment(Boolean.FALSE)
                .build();
        this.editBanCi(form);

        form.setType(BanCiType.appointment);
        this.editBanCi(form);

    }

    @Override
    public void initDefaultHospitalDoctorVisitLevel(Integer hospitalId) {
        DoctorVisitLevelForm form;
        form = DoctorVisitLevelForm.builder()
                .hospitalId(hospitalId)
                .code("PTH")
                .name("普通号")
                .status(Boolean.TRUE)
                .build();
        this.editDoctorVisitLevel(form);

        form = DoctorVisitLevelForm.builder()
                .hospitalId(hospitalId)
                .code("ZJH")
                .name("专家号")
                .status(Boolean.TRUE)
                .build();
        this.editDoctorVisitLevel(form);

    }

    @Override
    public List<HospitalDoctorBanCi> getDoctorBanCiList(BanCIListFrom form) {

        if (form.getHospitalId() == null || form.getHospitalId() <= 0 || form.getStartDate() == null || form.getEndDate() == null
        ) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate startDate = LocalDate.parse(form.getStartDate(), dateTimeFormatter);
        LocalDate endDate = LocalDate.parse(form.getEndDate(), dateTimeFormatter);
        if (startDate.isAfter(endDate)) {
            throw new NormalException(ResultEnums.PARAM_ERROR, "参数错误：开始时间在结束时间之后");
        }

        log.info("startDate = {}", GsonUtil.toJson(startDate));
        log.info("endDate = {}", GsonUtil.toJson(endDate));

        List<HospitalDoctorBanCi> doctorBanCiList = hospitalDoctorBanCiRepository.findAll(new Specification<HospitalDoctorBanCi>() {
            @Override
            public Predicate toPredicate(Root<HospitalDoctorBanCi> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();
                predicateList.add(builder.equal(root.get("hospitalId"), form.getHospitalId()));

                if(!StringUtils.isEmpty(form.getDoctorId())){
                    predicateList.add(builder.equal(root.get("doctorId"),form.getDoctorId()));
                }

                if (form.getType() != null) {
                    predicateList.add(builder.equal(root.get("banCiType"), form.getType()));
                }
                if (form.getDepId() != null && form.getDepId() > 0) {
                    predicateList.add(builder.equal(root.get("depId"), form.getDepId()));
                }
                if (!StringUtils.isEmpty(form.getKeyword())) {
                    Predicate p1 = builder.like(root.get("banCiCode"), "%" + form.getKeyword() + "%");
                    Predicate p2 = builder.like(root.get("banCiName"), "%" + form.getKeyword() + "%");
                    Predicate p3 = builder.like(root.get("doctorName"), "%" + form.getKeyword() + "%");
                    Predicate p4 = builder.like(root.get("depName"), "%" + form.getKeyword() + "%");
                    predicateList.add(builder.or(p1, p2, p3, p4));
                }

                predicateList.add(builder.greaterThanOrEqualTo(root.get("day"), startDate));
                predicateList.add(builder.lessThanOrEqualTo(root.get("day"), endDate));

                Predicate[] predicates = new Predicate[predicateList.size()];
                return query.where(predicateList.toArray(predicates)).getRestriction();
            }
        });
        return doctorBanCiList;
    }


    @Override
    public void adjustOnlineBanCi(AdjustOnlineBanCiForm form) {
        if (form.getHospitalId() == null || form.getHospitalId() <= 0
                || StringUtils.isEmpty(form.getDoctorId())
                || form.getBanCiId() == null || form.getBanCiId() <= 0
                || StringUtils.isEmpty(form.getDay())) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }

        LocalDate localDate = LocalDate.parse(form.getDay(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        HospitalDoctorBanCi banCi = hospitalDoctorBanCiRepository.findFirstByHospitalIdAndDoctorIdAndBanCiIdAndDay(form.getHospitalId(),
                form.getDoctorId(), form.getBanCiId(), localDate);
        HospitalBanCi hospitalBanCi = banCiRepository.findById(form.getBanCiId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        HospitalDoctor hospitalDoctor = doctorRepository.findById(form.getDoctorId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));

        if (banCi == null) {//新增
            banCi = new HospitalDoctorBanCi();
            BeanUtils.copyProperties(form, banCi);
            BeanUtils.copyProperties(hospitalDoctor, banCi);
            banCi.setBanCiId(hospitalBanCi.getId());
            banCi.setBanCiName(hospitalBanCi.getName());
            banCi.setStartTime(hospitalBanCi.getStartTime());
            banCi.setEndTime(hospitalBanCi.getEndTime());
            banCi.setDay(localDate);
            banCi.setBanCiType(BanCiType.online);
        } else {
            banCi.setPic(form.getPic());
            banCi.setVideo(form.getVideo());
            banCi.setVoice(form.getVoice());
        }

        hospitalDoctorBanCiRepository.save(banCi);
    }

    @Override
    public void adjustAppointmentBanCi(AdjustAppointmentBanCiForm form) {
        if (form.getHospitalId() == null || form.getHospitalId() <= 0
                || form.getLevelId() == null || form.getLevelId() <= 0
                || StringUtils.isEmpty(form.getDoctorId())
                || form.getBanCiId() == null || form.getBanCiId() <= 0
                || StringUtils.isEmpty(form.getDay())) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }

        LocalDate localDate = LocalDate.parse(form.getDay(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        HospitalDoctorBanCi banCi = hospitalDoctorBanCiRepository.findFirstByHospitalIdAndDoctorIdAndBanCiIdAndDay(form.getHospitalId(),
                form.getDoctorId(), form.getBanCiId(), localDate);
        HospitalBanCi hospitalBanCi = banCiRepository.findById(form.getBanCiId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        HospitalDoctor hospitalDoctor = doctorRepository.findById(form.getDoctorId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        HospitalDoctorVisitLevel level = visitLevelRepository.findById(form.getLevelId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));

        if (banCi == null) {//新增
            banCi = new HospitalDoctorBanCi();
            BeanUtils.copyProperties(form, banCi);
            BeanUtils.copyProperties(hospitalDoctor, banCi);
            banCi.setBanCiId(hospitalBanCi.getId());
            banCi.setBanCiName(hospitalBanCi.getName());
            banCi.setStartTime(hospitalBanCi.getStartTime());
            banCi.setEndTime(hospitalBanCi.getEndTime());
            banCi.setDay(localDate);
            banCi.setBanCiType(BanCiType.appointment);
            banCi.setLevelId(form.getLevelId());
            banCi.setLevelName(level.getName());
        } else {
            banCi.setAppointment(form.getAppointment());
        }

        hospitalDoctorBanCiRepository.save(banCi);
    }

    @Override
    public DoctorAvailableAppointmentDTO changeAvailableAppointmentCnt(AppointmentOrderForm form) {
        HospitalDoctorBanCi doctorBanCi = hospitalDoctorBanCiRepository.findById(form.getAppointId())
                .orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        if (doctorBanCi.getDoctorId().equals(form.getDoctorId())) {
            if (doctorBanCi.getAppointmentUsed() + form.getCnt() <= doctorBanCi.getAppointment()) {
                doctorBanCi.setAppointmentUsed(doctorBanCi.getAppointmentUsed() + form.getCnt());
            } else {
                throw new NormalException(ResultEnums.NOT_ENOUGH_SCHEDULING);
            }
        }
        hospitalDoctorBanCiRepository.save(doctorBanCi);
        DoctorAvailableAppointmentDTO dto = new DoctorAvailableAppointmentDTO();
        dto.setAppointId(form.getAppointId());
        dto.setDoctorId(form.getDoctorId());
        dto.setTotalCnt(doctorBanCi.getAppointment());
        dto.setRemain(doctorBanCi.getAppointment() - doctorBanCi.getAppointmentUsed());
        return dto;

    }

    @Override
    @Transactional
    public DoctorAvailableOnlineDTO changeAvailableOnlineCnt(OnlineOrderForm form) {
        HospitalDoctorBanCi doctorBanCi = hospitalDoctorBanCiRepository.findById(form.getAppointId())
                .orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        if (!doctorBanCi.getDoctorId().equals(form.getDoctorId())) {
            throw new NormalException(ResultEnums.NOT_EXIST);
        }
        DoctorAvailableOnlineDTO dto = new DoctorAvailableOnlineDTO();
        dto.setAppointId(form.getAppointId());
        dto.setDoctorId(form.getDoctorId());
        dto.setType(form.getType());

        List<HospitalDoctorBanCi> banCiList;
        switch (form.getType()) {
            case pic:
                if (doctorBanCi.getPicUsed() + form.getCnt() <= doctorBanCi.getPic()) {
                    doctorBanCi.setPicUsed(doctorBanCi.getPicUsed() + form.getCnt());
                } else {
                    throw new NormalException(ResultEnums.NOT_ENOUGH_SCHEDULING);
                }
                hospitalDoctorBanCiRepository.save(doctorBanCi);
                dto.setTotalCnt(doctorBanCi.getPic());
                dto.setRemain(doctorBanCi.getPic() - doctorBanCi.getPicUsed());
                break;
            case voice:
                if (doctorBanCi.getVoiceUsed() + form.getCnt() > doctorBanCi.getVoice()) {
                    throw new NormalException(ResultEnums.NOT_ENOUGH_SCHEDULING);
                }
                banCiList = hospitalDoctorBanCiRepository.findAllByHospitalIdAndDoctorIdAndDayAndBanCiId(doctorBanCi.getHospitalId(),
                        doctorBanCi.getDoctorId(), doctorBanCi.getDay(), doctorBanCi.getBanCiId());

                for (HospitalDoctorBanCi banCi:banCiList){
                    banCi.setVoiceUsed(banCi.getVoiceUsed()+form.getCnt());
                }
                hospitalDoctorBanCiRepository.saveAll(banCiList);

                dto.setTotalCnt(doctorBanCi.getVoice());
                dto.setRemain(doctorBanCi.getVoice() - doctorBanCi.getVoiceUsed());
                break;
            case video:

                if (doctorBanCi.getVideoUsed() + form.getCnt() > doctorBanCi.getVideo()) {
                    throw new NormalException(ResultEnums.NOT_ENOUGH_SCHEDULING);
                }

                banCiList = hospitalDoctorBanCiRepository.findAllByHospitalIdAndDoctorIdAndDayAndBanCiId(doctorBanCi.getHospitalId(),
                        doctorBanCi.getDoctorId(), doctorBanCi.getDay(), doctorBanCi.getBanCiId());

                for (HospitalDoctorBanCi banCi:banCiList){
                    banCi.setVideoUsed(banCi.getVideoUsed()+form.getCnt());
                }
                hospitalDoctorBanCiRepository.saveAll(banCiList);

                dto.setTotalCnt(doctorBanCi.getVideo());
                dto.setRemain(doctorBanCi.getVideo() - doctorBanCi.getVideoUsed());
                break;
            default:
                throw new NormalException(ResultEnums.PARAM_ERROR);
        }

        return dto;
    }

    private void initDoctorVisit(Integer hospitalId, String doctorId) {
        DoctorDTO info = doctorService.info(doctorId);

        List<HospitalBanCi> banCiList = banCiRepository.findAll();
        for (HospitalBanCi banCi : banCiList) {
            HospitalDoctorVisitSetting doctorVisitSetting = HospitalDoctorVisitSetting.builder()
                    .hospitalId(hospitalId)
                    .doctorId(doctorId)
                    .doctorName(info.getDoctorName())
                    .depId(info.getDepId())
                    .banCiId(banCi.getId())
                    .pic(0)
                    .video(0)
                    .voice(0)
                    .wb(info.getWb())
                    .py(info.getPy())
                    .build();

            hospitalDoctorVisitSettingRepository.save(doctorVisitSetting);

        }
    }

    private Map<Integer, HospitalBanCi> getHospitalBanCiIdMap(Integer hospitalId, BanCiType banCiType) {
        List<HospitalBanCi> allByHospitalId = banCiRepository.findAllByHospitalIdAndStatusAndTypeOrderByStartTime(hospitalId, true, banCiType);
        if (allByHospitalId.size() == 0) {
            throw new NormalException(ResultEnums.HOSPITAL_SECLUDING_NOT_SET);
        }
        Map<Integer, HospitalBanCi> map = new HashMap<>();
        for (HospitalBanCi hospitalBanCi : allByHospitalId) {
            map.put(hospitalBanCi.getId(), hospitalBanCi);
        }
        return map;
    }

    private Map<String, HospitalBanCi> getHospitalBanCiNameMap(Integer hospitalId, BanCiType banCiType) {
        List<HospitalBanCi> allByHospitalId = banCiRepository.findAllByHospitalIdAndStatusAndTypeOrderByStartTime(hospitalId, true, banCiType);
        if (allByHospitalId.size() == 0) {
            throw new NormalException(ResultEnums.HOSPITAL_SECLUDING_NOT_SET);
        }
        Map<String, HospitalBanCi> map = new HashMap<>();
        for (HospitalBanCi hospitalBanCi : allByHospitalId) {
            map.put(hospitalBanCi.getName(), hospitalBanCi);
        }
        return map;
    }

    private BaseFeignClient baseFeignClient;

    private HospitalBanCiRepository banCiRepository;

    private HospitalDoctorVisitSettingRepository hospitalDoctorVisitSettingRepository;

    private HospitalDoctorVisitLevelRepository visitLevelRepository;

    private HospitalDoctorBanCiRepository hospitalDoctorBanCiRepository;

    private HospitalDoctorService doctorService;

    private HospitalDoctorRepository doctorRepository;

    private HospitalDepartmentRepository departmentRepository;


    @Autowired
    public void setBanCiRepository(HospitalBanCiRepository banCiRepository) {
        this.banCiRepository = banCiRepository;
    }

    @Autowired
    public void setBaseFeignClient(BaseFeignClient baseFeignClient) {
        this.baseFeignClient = baseFeignClient;
    }

    @Autowired
    public void setDoctorVisitSettingRepository(HospitalDoctorVisitSettingRepository hospitalDoctorVisitSettingRepository) {
        this.hospitalDoctorVisitSettingRepository = hospitalDoctorVisitSettingRepository;
    }

    @Autowired
    public void setVisitLevelRepository(HospitalDoctorVisitLevelRepository visitLevelRepository) {
        this.visitLevelRepository = visitLevelRepository;
    }

    @Autowired
    public void setHospitalDoctorBanCiRepository(HospitalDoctorBanCiRepository hospitalDoctorBanCiRepository) {
        this.hospitalDoctorBanCiRepository = hospitalDoctorBanCiRepository;
    }

    @Autowired
    public void setDoctorService(HospitalDoctorService doctorService) {
        this.doctorService = doctorService;
    }

    @Autowired
    public void setDoctorRepository(HospitalDoctorRepository doctorRepository) {
        this.doctorRepository = doctorRepository;
    }

    @Autowired
    public void setDepartmentRepository(HospitalDepartmentRepository departmentRepository) {
        this.departmentRepository = departmentRepository;
    }
}
